package com.qd.admin.controller;

import com.cdqidi.dto.ApiResult;
import com.cdqidi.dto.ApiResultWrapper;
import com.qd.common.sys.annotation.OperatorInfo;
import com.qd.common.sys.enums.OperatorType;
import com.qd.system.util.FileConfirmUtil;
import com.qd.upload.config.FileConfirmReq;
import com.qd.upload.config.UploadDTO;
import com.qd.upload.service.UploadServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 上传
 *
 * @author sjk
 */
@RestController
@Slf4j
@Validated
@RequiredArgsConstructor
public class UploadController {

    private final UploadServiceImpl uploadService;
    private final UploadDTO uploadDto;
    private final HttpServletResponse response;
    private final HttpServletRequest request;

    /**
     * 文件上传
     *
     * @param files 文件列表，支持多个
     * @return 成功 or 失败
     */
    @RequestMapping(value = "/sys/sysFile/uploads")
    public ApiResultWrapper<Object> upload(@RequestParam("files") MultipartFile[] files) {
        if (null == files || files.length < 1) {
            return ApiResult.error("文件不能为空");
        }
        final List<String> list = uploadService.uploadFiles(files);
        return ApiResult.success(list);
    }

    /**
     * 文件确认
     *
     * @param fileConfirmReq 文件列表，支持多个
     * @return 成功 or 失败
     */
    @PostMapping(value = "/sys/sysFile/confirms")
    public ApiResultWrapper<Object> confirms(@Valid @RequestBody FileConfirmReq fileConfirmReq) {
        final boolean autoTypeKey = fileConfirmReq.getInfo().isAutoTypeKey();
        if (autoTypeKey) {
            final String bKey = fileConfirmReq.getInfo().getBKey();
            fileConfirmReq = FileConfirmUtil.buildFileConfirmReq(fileConfirmReq.getFiles(), fileConfirmReq.getInfo().getAttachmentType());
            if (StringUtils.hasLength(bKey)) {
                fileConfirmReq.getInfo().setBKey(bKey);
            }
        }
        return ApiResult.success(uploadService.fileConfirms(fileConfirmReq));
    }

    /**
     * 清理上传文件夹
     *
     * @return 成功 or 失败
     */
    @DeleteMapping(value = "/sys/sysFile/delete")
    @OperatorInfo(title = "清理上传文件夹", operator = OperatorType.DELETE)
    public ApiResultWrapper<Object> delete() {
        uploadService.delete();
        return ApiResult.success();
    }

    /**
     * 文件下载，指定文件名
     *
     * @param path     文件路径
     * @param fileName 文件名字
     */
    @GetMapping(value = "/sys/sysFile/downloadFile")
    public void downloadFile(String path, String fileName) {
        final String dPath = URLDecoder.decode(path, StandardCharsets.UTF_8);
        final File file = new File(uploadDto.getOfficialPath() + File.separator + dPath);
        // 如果文件存在，则进行下载
        if (file.exists()) {
            response.setContentType(request.getServletContext().getMimeType(fileName));
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
            try {
                try (FileInputStream fis = new FileInputStream(file)) {
                    try (BufferedInputStream bis = new BufferedInputStream(fis)) {
                        OutputStream os = response.getOutputStream();
//                        try (OutputStream os = response.getOutputStream()) {
                        final byte[] b = new byte[1024];
                        int len;
                        while ((len = bis.read(b)) > 0) {
                            os.write(b, 0, len);
                        }
                        os.flush();
//                        }
                    }
                }
            } catch (Exception ignored) {
            }
        } else {
            log.info("文件不存在： {}", file.getAbsolutePath());
        }
    }

    /**
     * 文件下载
     *
     * @param fileBaseName 文件名字
     */
    @GetMapping(value = "/sys/sysFile/download/{fileBaseName}/**", produces = "application/json;charset=UTF-8")
    public void download(@PathVariable String fileBaseName) {
        final String path =
                request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).toString();
        final String bestMatchingPattern =
                request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE).toString();

        final String arguments = new AntPathMatcher().extractPathWithinPattern(bestMatchingPattern, path);

        String fileName;
        if (!arguments.isEmpty()) {
            fileName = fileBaseName + '/' + arguments;
        } else {
            fileName = fileBaseName;
        }
        if (log.isDebugEnabled()) {
            log.debug("filePath: {}", fileName);
        }
        final File file = new File(uploadDto.getOfficialPath() + File.separator + fileName);
        // 如果文件存在，则进行下载
        if (file.exists()) {
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            String fName = URLEncoder.encode(file.getName(), StandardCharsets.UTF_8).replace("\\+", "%20");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fName);
            // 实现文件下载
            try {
                try (FileInputStream fis = new FileInputStream(file)) {
                    try (BufferedInputStream bis = new BufferedInputStream(fis)) {
                        OutputStream os = response.getOutputStream();
//                        try (OutputStream os = response.getOutputStream()) {
                        final byte[] b = new byte[1024];
                        int len;
                        while ((len = bis.read(b)) > 0) {
                            os.write(b, 0, len);
                        }
                        os.flush();
                    }
//                    }
                }
            } catch (Exception ignored) {
            }
        }
    }
}
